﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml.XPath;
using Flagwind.Data;

namespace Flagwind.Utilities.DataBuilder.Controls
{
	public partial class EntityBuilder : UserControl
	{
		#region 私有字段

		private DataTable _mapInfo;
		private bool _isSqlServer2000;
		private string _dataSourceProductVersion;
		private string _dataSourceProductName;
		private string _defaultTableScheme = "dbo";

		#endregion

		#region 私有常量

		private const string TEMPLETE_PATH = "./Templets/Entity.xml";

		#endregion

		#region 公共属性

		/// <summary>
		/// 默认的表架构。
		/// </summary>
		public string DefaultTableScheme
		{
			get
			{
				return _defaultTableScheme;
			}
			set
			{
				_defaultTableScheme = value;
			}
		}

		/// <summary>
		/// 获取或设置命名空间。
		/// </summary>
		public string Namespace
		{
			get
			{
				return txtNamespace.Text;
			}
			set
			{
				txtNamespace.Text = value;
			}
		}

		/// <summary>
		/// 获取或设置前缀。
		/// </summary>
		public string Prefix
		{
			get
			{
				return txtPrefix.Text;
			}
			set
			{
				txtPrefix.Text = value;
			}
		}

		/// <summary>
		/// 获取或设置输出路径。
		/// </summary>
		public string OutputPath
		{
			get
			{
				return txtOutputPath.Text;
			}
			set
			{
				txtOutputPath.Text = value;
			}
		}

		/// <summary>
		/// 获取或设置输出日志。
		/// </summary>
		public string OutputLog
		{
			get
			{
				return txtOutputLog.Text;
			}
			set
			{
				txtOutputLog.Text = value;
			}
		}

		/// <summary>
		/// 获取或设置数据库名称。
		/// </summary>
		public string DatabaseName
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取数据访问提供程序。
		/// </summary>
		public IDataProvider DataProvider
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取源'表'列表。
		/// </summary>
		public List<string> SourceTables
		{
			get;
			private set;
		}

		/// <summary>
		/// 获取源'视图'列表。
		/// </summary>
		public List<string> SourceViews
		{
			get;
			private set;
		}

		#endregion

		#region 构造方法

		/// <summary>
		/// 初始化 EntityBuilder 类的新实例。
		/// </summary>
		/// <param name="dataProvider">数据访问提供程序。</param>
		/// <param name="databaseName">数据库名称。</param>
		/// <param name="tables">表集合。</param>
		/// <param name="views">视图集合。</param>
		public EntityBuilder(IDataProvider dataProvider, string databaseName, List<string> tables, List<string> views)
		{
			// 初始化组件
			InitializeComponent();

			// 初始化模块
			this.InitModule();

			// 初始化映射信息
			this.InitMapInfo();

			this.DataProvider = dataProvider;
			this.DatabaseName = databaseName;
			this.SourceTables = tables;
			this.SourceViews = views;

			if(this.DataProvider.ProviderType == DataProviderType.SqlServer)
			{
				_dataSourceProductName = "Microsoft SQL Server";

				var buffer = this.DataProvider.ExecuteScalar("SELECT SERVERPROPERTY('ProductVersion')").ToString();
				int version = Convert.ToInt32(buffer.Split('.')[0]);

				if(version <= 8)
				{
					this._isSqlServer2000 = true;
				}
			}
		}

		#endregion

		#region 事件方法

		/// <summary>
		/// 当生成器加载完毕时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void EntityBuilder_Load(object sender, EventArgs e)
		{
			// 追加表
			if(this.SourceTables != null)
			{
				foreach(string tableName in this.SourceTables)
				{
					var row = this._mapInfo.NewRow();

					row[0] = true;
					row[1] = tableName;
					row[2] = "表";
					row[3] = this.RemoveScheme(tableName);
					row[4] = "<默认>";

					this._mapInfo.Rows.Add(row);
				}
			}

			// 追加视图
			if(this.SourceViews != null)
			{
				foreach(string viewName in this.SourceViews)
				{
					DataRow row2 = this._mapInfo.NewRow();
					row2[0] = true;
					row2[1] = viewName;
					row2[2] = "视图";
					row2[3] = this.RemoveScheme(viewName);
					row2[4] = "<默认>";

					this._mapInfo.Rows.Add(row2);
				}
			}

			this._mapInfo.AcceptChanges();

			this.grdDefault.AutoGenerateColumns = false;
			this.grdDefault.DataSource = this._mapInfo;

			this.Namespace = this.DatabaseName;
		}

		/// <summary>
		/// 当数据表格单元格点击时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void grdDefault_CellContentClick(object sender, DataGridViewCellEventArgs e)
		{
			var gridView = (DataGridView) sender;

			// 预览代码
			if(gridView.Columns[e.ColumnIndex].Name == "colPreview")
			{
				var rowView = (DataRowView)gridView.CurrentRow.DataBoundItem;
				var tableName = rowView[1].ToString();
				var className = rowView[3].ToString();
				var sql = this.GetSqlMapText(rowView);
				var classText = this.GetClassText(className, sql, tableName, this.GetMapType(rowView[2].ToString()));

				var previewText = this.GetHeaderText() + classText;
				var frmCodePreview = new FCodePreview(previewText);
				frmCodePreview.ShowDialog();
			}
		}

		/// <summary>
		/// 当点击生成按钮时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void btnGenerate_Click(object sender, EventArgs e)
		{
			if(!this.OutputPath.EndsWith(@"\"))
				this.OutputPath = this.OutputPath + @"\";

			// 创建目录
			if(!Directory.Exists(this.OutputPath))
				Directory.CreateDirectory(this.OutputPath);

			// 设置生成按钮状态
			btnGenerate.Enabled = false;

			// 设置鼠标状态为等待状态
			this.Cursor = Cursors.WaitCursor;

			// 设置 DataView
			var dataView = this._mapInfo.DefaultView;
			dataView.RowFilter = "Selected = True";

			// 设置进度条最大值
			this.progressBar.Maximum = dataView.Count;

			var count = 0;

			this.OutputLog = string.Empty;

			#region 开始生成

			foreach(DataRowView dataRow in dataView)
			{
				var entityMap = this.GetMapType(dataRow[2].ToString());
				var entityName = dataRow["EntityName"].ToString();
				var tableName = dataRow["MappingName"].ToString();
				var outputFile = dataRow["OutputFile"].ToString();

				if(entityMap == EntityMapType.SqlMap)
					tableName = this.Namespace + "." + tableName;

				if(outputFile == "<默认>")
					outputFile = this.OutputPath + entityName + ".cs";

				count += 1;

				// 设置日志
				this.OutputLog = this.OutputLog + "正在生成第 " + count + " 个实体类文件： " + outputFile + "\r\n";

				// 设置进度条值
				this.progressBar.Value = count;

				Application.DoEvents();

				string sql = dataRow["SQLMap"].ToString();

				if(string.IsNullOrEmpty(sql))
					sql = "SELECT * FROM " + this.GetSchemeTableName(tableName);

				var classText = this.GetClassText(entityName, sql, tableName, entityMap);
				var outputText = this.GetHeaderText() + classText;

				File.WriteAllText(outputFile, outputText, Encoding.UTF8);

				if(entityMap == EntityMapType.SqlMap)
				{
					//this.CreateEntitySqlMapFile(this.propWindow.OutputPath);
					//this.WriteEntitySQLMapFile(this.propWindow.OutputPath, this.propWindow.DefaultNamespace, current["MappingName"].ToString(), sql);
					//txtOutputLog = this.txtOutputLog;
					//txtOutputLog.Text = txtOutputLog.Text + "向[SQL-实体类]映射文件EntitySqlMap.config 写入用户查询 " + str5 + "\r\n";
				}

			}

			btnGenerate.Enabled = true;
			this.Cursor = Cursors.Default;

			MessageBox.Show("共生成 " + count + " 个实体类文件！", "生成器");

			#endregion
		}

		/// <summary>
		/// 当点击替换前缀时调用。
		/// </summary>
		/// <param name="sender">事件发送者。</param>
		/// <param name="e">事件参数。</param>
		private void lnlReplace_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			if(!string.IsNullOrEmpty(this.Prefix))
			{
				this.RemoveEntityPrefix(this.Prefix);
			}
		}

		#endregion

		#region 私有方法

		/// <summary>
		/// 初始化模块。
		/// </summary>
		private void InitModule()
		{
			this.Text = "　实体生成器　";
			this.Dock = DockStyle.Fill;
		}

		/// <summary>
		/// 初始化映射信息。
		/// </summary>
		private void InitMapInfo()
		{
			this._mapInfo = new DataTable("mapInfo");

			this._mapInfo.Columns.Add("Selected", typeof(bool));
			this._mapInfo.Columns.Add("MappingName", typeof(string));
			this._mapInfo.Columns.Add("MappingType", typeof(string));
			this._mapInfo.Columns.Add("EntityName", typeof(string));
			this._mapInfo.Columns.Add("OutputFile", typeof(string));
			this._mapInfo.Columns.Add("SQLMap", typeof(string));

			this._mapInfo.Columns[0].ReadOnly = false;
			this._mapInfo.Columns[1].ReadOnly = true;
			this._mapInfo.Columns[3].ReadOnly = false;

			this._mapInfo.PrimaryKey = new DataColumn[] { this._mapInfo.Columns[1] };

		}

		/// <summary>
		/// 根据指定的数据行获取一个 Sql 脚本。
		/// </summary>
		private string GetSqlMapText(DataRowView rowView)
		{
			string result = rowView["SQLMap"].ToString();

			if(string.IsNullOrEmpty(result))
			{
				var type = rowView["MappingType"].ToString();

				if(type.Equals("表") || type.Equals("视图"))
				{
					result = string.Format("SELECT * FROM {0}", rowView["MappingName"]);
				}
			}

			return result;
		}

		/// <summary>
		/// 根据指定的数据源名称获取映射类型。
		/// </summary>
		/// <param name="dataSource">数据源名称。</param>
		/// <returns>映射类型。</returns>
		private EntityMapType GetMapType(string dataSource)
		{
			if(dataSource.Equals("表"))
			{
				return EntityMapType.Table;
			}
			else if(dataSource.Equals("视图"))
			{
				return EntityMapType.View;
			}

			return EntityMapType.SqlMap;
		}

		/// <summary>
		/// 获取文件头文本。
		/// </summary>
		/// <returns>头文件文本。</returns>
		private string GetHeaderText()
		{
			// 加载 XML 文档对象
			var document = XDocument.Load(TEMPLETE_PATH);

			var headerText = document.XPathSelectElement("/template/header").Value;

			headerText = headerText.Replace("%Version%", Utility.AppVersion)
								   .Replace("%DateTime%", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

			return headerText;
		}

		/// <summary>
		/// 获取实体类文本。
		/// </summary>
		/// <param name="className">实体类名称。</param>
		/// <param name="sql">SQL 语句</param>
		/// <param name="mappingName">映射的名称。</param>
		/// <param name="mappingType">映射的类型。</param>
		/// <returns>实体类文本。</returns>
		private string GetClassText(string className, string sql, string mappingName, EntityMapType mappingType)
		{
			var dataSet = this.DataProvider.ExecuteDataSetSchema(sql, CommandType.Text, null);
			var dataTable = dataSet.Tables[0];

			string str2 = string.Empty;
			string langLine = string.Empty;
			string classText = string.Empty;
			string propertyText = string.Empty;
			string setFieldNamesText = string.Empty;
			string newMappingName = mappingName;

			className = className.Replace(".", "_").Replace(" ", "_").Replace("[", "").Replace("]", "").Replace("#", "").Replace("@", "");

			// 加载 XML 文档对象
			var document = XDocument.Load(TEMPLETE_PATH);

			classText = document.XPathSelectElement("/template/body/main").Value;
			propertyText = document.XPathSelectElement("/template/body/property").Value;
			setFieldNamesText = document.XPathSelectElement("/template/body/setFieldNames").Value;

			// 替换 NameSpace
			classText = classText.Replace("[NameSpace]", this.Namespace);

			// 替换 ClassName
			classText = classText.Replace("[ClassName]", className);

			#region 替换 Scheme

			var schemeName = string.Empty;
			
			var dotIndex = mappingName.IndexOf(".");

			if(dotIndex > 0)
			{
				schemeName = mappingName.Substring(0, dotIndex);

				// 默认的架构 dbo 不做处理
				if(schemeName.ToLower().Equals(this.DefaultTableScheme.ToLower()))
					schemeName = string.Empty;

				newMappingName = mappingName.Substring(dotIndex + 1);
			}
			else if(dotIndex == 0)
			{
				newMappingName = mappingName.Substring(1);
			}

			// 替换 Scheme
			classText = classText.Replace("[Scheme]", schemeName);

			#endregion

			// 替换 MappingType
			classText = classText.Replace("[MappingType]", "MappingType." + mappingType.ToString());

			// 替换 MappingName
			classText = classText.Replace("[MappingName]", newMappingName);

			#region 替换 Identity / PrimaryKey

			var identityText = string.Empty;
			var primaryKeyText = string.Empty;

			foreach(DataColumn column in dataTable.PrimaryKey)
			{
				primaryKeyText = primaryKeyText + "this.PrimaryKey.Add(\"" + column.ColumnName + "\");";

				if(column.AutoIncrement)
				{
					identityText = "this.Identity = \"" + column.ColumnName + "\";";
				}
			}

			classText = classText.Replace("%Identity%", identityText).Replace("%PrimaryKey%", primaryKeyText);

			#endregion

			#region 替换 Properties

			// 所有属性文本
			string propertiesText = string.Empty;

			DataTable fieldDescriptions = null;

			if(mappingType == EntityMapType.Table)
			{
				fieldDescriptions = this.GetFieldDescriptions(this._isSqlServer2000, newMappingName);
			}

			// 所有属性，以‘，’号分隔
			string propertyNames = string.Empty;

			foreach(DataColumn column in dataTable.Columns)
			{
				var lengthText = column.MaxLength > 0 ? (" ," + column.MaxLength) : "";

				string fieldDescText = string.Empty;

				if(fieldDescriptions != null)
				{
					var fieldRow = fieldDescriptions.Rows.Cast<DataRow>().FirstOrDefault(row => row[1].ToString().Equals(column.ColumnName));

					fieldDescText = fieldRow != null ? (!string.IsNullOrEmpty(fieldRow[2].ToString()) ? fieldRow[2].ToString() : fieldRow[1].ToString()) : "";
				}

				propertiesText += propertyText.Replace("[Name]", column.ColumnName.Replace(" ", ""))
					.Replace("<T>", this.GetPropertyType(column))
					.Replace("[,Length]", lengthText)
					.Replace("[Description]", fieldDescText);

				propertyNames = propertyNames + "\"" + column.ColumnName + "\",";
			}

			setFieldNamesText = setFieldNamesText.Replace("%PropertyNames%", propertyNames.TrimEnd(','));

			classText = classText.Replace("%Properties%", propertiesText);

			classText = classText.Replace("%SetFieldNames%", setFieldNamesText);

			#endregion

			return classText;
		}

		private string GetPropertyType(DataColumn column)
		{
			var type = column.DataType.Name;

			if(column.AllowDBNull && !column.DataType.IsClass)
				type = string.Format("{0}?", type);

			return type;
		}

		/// <summary>
		/// 根据指定的表明获取所有字段描述信息。
		/// </summary>
		/// <param name="isSqlServer2000">是否为 SqlServer 2000。</param>
		/// <param name="tableName">表名。</param>
		/// <returns>一个包含所有字段描述信息的表。</returns>
		private DataTable GetFieldDescriptions(bool isSqlServer2000, string tableName)
		{
			var sql = string.Empty;

			if(isSqlServer2000)
			{
				sql = @"SELECT 
							[Table Name] = i_s.TABLE_NAME, 
							[Column Name] = i_s.COLUMN_NAME, 
							[Description] = s.value 
						FROM 
							INFORMATION_SCHEMA.COLUMNS i_s 
						LEFT OUTER JOIN 
							sysproperties s 
						ON 
							s.id = OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME) 
							AND s.smallid = i_s.ORDINAL_POSITION 
							AND s.name = 'MS_Description' 
						WHERE 
							OBJECTPROPERTY(OBJECT_ID(i_s.TABLE_SCHEMA+'.'+i_s.TABLE_NAME), 'IsMsShipped')=0 
							AND i_s.TABLE_NAME = '@@table_name'   
						ORDER BY 
							i_s.TABLE_NAME, i_s.ORDINAL_POSITION";
			}
			else
			{
				sql = @"SELECT 
							[Table Name] = OBJECT_NAME(c.object_id), 
							[Column Name] = c.name, 
							[Description] = ex.value 
						FROM 
							sys.columns c 
						LEFT OUTER JOIN 
							sys.extended_properties ex 
						ON 
							ex.major_id = c.object_id 
							AND ex.minor_id = c.column_id 
							AND ex.name = 'MS_Description' 
						WHERE 
							OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0 
							 AND OBJECT_NAME(c.object_id) = '@@table_name' 
						ORDER 
							BY OBJECT_NAME(c.object_id), c.column_id";
			}

			sql = sql.Replace("@@table_name", tableName);

			var dataSet = this.DataProvider.ExecuteDataSet(sql);

			return dataSet.Tables[0];
		}

		/// <summary>
		/// 移除指定表名的架构信息。
		/// </summary>
		/// <param name="tableName">表名。</param>
		/// <returns>移除后的信息。</returns>
		private string RemoveScheme(string tableName)
		{
			var result = string.Empty;
			var dotIndex = tableName.IndexOf(".");

			if(dotIndex > 0)
			{
				result = tableName.Substring(dotIndex + 1);
			}
			else if(dotIndex == 0)
			{
				result = tableName.Substring(1);
			}

			return result;
		}

		/// <summary>
		/// 获取带 Scheme 的表名。
		/// </summary>
		/// <param name="tableName">源表名。</param>
		/// <returns>新表名。</returns>
		private string GetSchemeTableName(string tableName)
		{
			string strTableName = tableName;

			int dotIndex = tableName.IndexOf('.');

			if(dotIndex > 0)
			{
				string strScheme = tableName.Substring(0, dotIndex);

				if(strScheme.ToLower() == _defaultTableScheme)
				{
					strScheme = "";
				}

				strTableName = tableName.Substring(dotIndex + 1);

				if(strScheme == "")
				{
					return ("[" + strTableName + "]");
				}

				return string.Format("[{0}].[{1}]", strScheme, strTableName);
			}

			if(dotIndex == 0)
			{
				return ("[" + strTableName.Substring(1) + "]");
			}

			return ("[" + strTableName + "]");
		}

		/// <summary>
		/// 移除映射实体前缀。
		/// </summary>
		/// <param name="prefix">前缀名。</param>
		private void RemoveEntityPrefix(string prefix)
		{
			foreach(DataRow dataRow in this._mapInfo.Rows)
			{
				var entityName = dataRow["EntityName"].ToString();

				if(entityName.StartsWith(prefix))
				{
					dataRow["EntityName"] = entityName.TrimStart(prefix.ToCharArray());
				}
			}

			this._mapInfo.AcceptChanges();
		}

		#endregion
	}
}
